﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Wrapper.Direct3D;
using System.Drawing;

namespace Vortex.Drawing {
	/// <summary>
	/// Canvas2D is a main drawing class it contains all of drawing function. It is statefull. You can change its states to modify drawing output
	/// </summary>
	public sealed partial class Canvas2D : StateScope {
		public const float DEFAULT_Z = 0.0f;

		private DrawingDevice _device;
		private IDrawingTarget _target;
		private Renderer2D _renderer;

		private Vector2 _size;
		private Rect _region;

		private CanvasStates _states = new CanvasStates();
		private CanvasStateChanges _stateChanges = CanvasStateChanges.None;

		private CanvasBrush _currentBrush;

		private float _Z = 0.0f;

		/// <summary>
		/// Initializes a new instance of the <see cref="Canvas2D"/> class.
		/// </summary>
		/// <param name="target">The canvas target.</param>
		/// <param name="renderer">The renderer to send all graphics.</param>
		internal Canvas2D(IDrawingTarget target, Renderer2D renderer) {
			_target = target;
			_renderer = renderer;
			_device = renderer.Device;

			//configure canvas size
			_target.OnCreateSurface += delegate(IDrawingTarget t) {
				UpdateCanvasExtent();
			};

			UpdateCanvasExtent();

			//and update size on beginning
			SetDefaults();
		}

		/// <summary>
		/// Updates canvas extent (size and region).
		/// </summary>
		private void UpdateCanvasExtent() {
			_size = (Vector2)_target.Size;
			_region = new Rect(Vector2.Zero, _size);
		}

		/// <summary>
		/// Adds the change flags to overall changes.
		/// </summary>
		/// <param name="changes">The change flags.</param>
		private void AddStateChanges(CanvasStateChanges changes) {
			_stateChanges = _stateChanges | changes;
		}

		/// <summary>
		/// Gets a value indicating whether this canvas has state changes.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this canvas has state changes; otherwise, <c>false</c>.
		/// </value>
		internal bool HasStateChanges {
			get { return _stateChanges != CanvasStateChanges.None; }
		}

		/// <summary>
		/// Gets or sets the state change flags for this canvas.
		/// </summary>
		/// <value>The state change flags.</value>
		internal CanvasStateChanges StateChanges {
			get { return _stateChanges; }
			set { _stateChanges = value; }
		}

		/// <summary>
		/// Gets the states of canvas.
		/// </summary>
		/// <value>The canvas states.</value>
		internal CanvasStates States {
			get { return _states; }
		}

		#region Private Methods

		/// <summary>
		/// Fills the brush coordinates for vectors.
		/// </summary>
		/// <param name="vertices">The vertices to fill data.</param>
		private unsafe void FillBrushCoords(Vertex2D* vertices) {
			Rect region = _currentBrush.TransformedRegion;
			vertices[0].UV2 = region.LeftTop;
			vertices[1].UV2 = region.RightTop;
			vertices[2].UV2 = region.RightBottom;
			vertices[3].UV2 = region.LeftBottom;
		}

		#endregion

		#region Control Stuff

		/// <summary>
		/// Sets the canvas defaults.
		/// </summary>
		public void SetDefaults() {
			Brush = CanvasBrush.Default;
			AlphaTest = AlphaTest.Always;
			ZEffect = ZEffect.None;
			PixelShader = PixelShader.Default;
			VertexShader = VertexShader.Default;
			Blending = Blending.Normal;
			Clipping = Clipping.None;

			Z = DEFAULT_Z;
			ResetTransform();
		}

		#endregion

		#region General properties

		/// <summary>
		/// Gets the canvas target.
		/// </summary>
		/// <value>The drawing target.</value>
		public IDrawingTarget Target {
			get { return _target; }
		}

		/// <summary>
		/// Gets the size of canvas in logical view units.
		/// </summary>
		/// <value>The size vector.</value>
		public Vector2 Size {
			get { return _size; }
		}

		/// <summary>
		/// Gets the current canvas region in logical view units
		/// </summary>
		/// <value>The canvas region.</value>
		public Rect Region {
			get { return _region; }
		}

		/// <summary>
		/// Gets the width of canvas.
		/// </summary>
		/// <value>The width of canvas.</value>
		public float Width {
			get { return _size.X; }
		}

		/// <summary>
		/// Gets the height of canvas.
		/// </summary>
		/// <value>The height of canvas.</value>
		public float Height {
			get { return _size.Y; }
		}

		#endregion

		#region Clear Methods

		/// <summary>
		/// Clears the whole canvas with specified color.
		/// </summary>
		/// <param name="color">The color.</param>
		public void Clear(ColorU color) {
			_renderer.SetCanvas(this);
			_renderer.Flush();
			_device.ApiRef.Clear(null, D3DClearFlags.Target, color, 0, 0);
		}

		/// <summary>
		/// Clears the whole canvas z-buffer with specified z.
		/// </summary>
		/// <param name="z">The z value.</param>
		public void Clear(float z) {
			_renderer.SetCanvas(this);
			_renderer.Flush();
			_device.ApiRef.Clear(null, D3DClearFlags.ZBuffer, 0, z, 0);
		}

		/// <summary>
		/// Clears the whole canvas and z-buffer with specified color and z.
		/// </summary>
		/// <param name="color">The color.</param>
		/// <param name="z">The z value.</param>
		public void Clear(ColorU color, float z) {
			_renderer.SetCanvas(this);
			_renderer.Flush();
			_device.ApiRef.Clear(null, D3DClearFlags.Target | D3DClearFlags.ZBuffer, color, z, 0);
		}

		/// <summary>
		/// Clears the specified region of canvas.
		/// </summary>
		/// <param name="region">The region.</param>
		/// <param name="color">The color.</param>
		public void Clear(Rectangle region, ColorU color) {
			_renderer.SetCanvas(this);
			_renderer.Flush();
			_device.ApiRef.Clear(region, D3DClearFlags.Target, color, 0, 0);
		}

		/// <summary>
		/// Clears the specified region of z-buffer.
		/// </summary>
		/// <param name="region">The region.</param>
		/// <param name="z">The z.</param>
		public void Clear(Rectangle region, float z) {
			_renderer.SetCanvas(this);
			_renderer.Flush();
			_device.ApiRef.Clear(region, D3DClearFlags.ZBuffer, 0, z, 0);
		}

		/// <summary>
		/// Clears the specified region of canvas and z-buffer.
		/// </summary>
		/// <param name="region">The region.</param>
		/// <param name="color">The color.</param>
		/// <param name="z">The z.</param>
		public void Clear(Rectangle region, ColorU color, float z) {
			_renderer.SetCanvas(this);
			_renderer.Flush();
			_device.ApiRef.Clear(region, D3DClearFlags.Target | D3DClearFlags.ZBuffer, color, z, 0);
		}

		#endregion

		#region Inline Properties

		/// <summary>
		/// Gets or sets the Z value for canvas primitives.
		/// </summary>
		/// <value>The Z value.</value>
		public float Z {
			get { return _Z; }
			set { _Z = value;}
		}

		#endregion

		#region State Properites

		/// <summary>
		/// Gets or sets the vertex shader for this canvas.
		/// </summary>
		/// <value>The vertex shader instance.</value>
		public VertexShader VertexShader {
			get { return _states.VertexShader; }
			set {
				_states.VertexShader = value;
				AddStateChanges(CanvasStateChanges.VertexShader);
			}
		}

		/// <summary>
		/// Gets or sets the pixel shader for this canvas.
		/// </summary>
		/// <value>The pixel shader instance.</value>
		public PixelShader PixelShader {
			get { return _states.PixelShader; }
			set {
				_states.PixelShader = value;
				AddStateChanges(CanvasStateChanges.PixelShader);
			}
		}

		/// <summary>
		/// Gets or sets the blending mode for this canvas.
		/// </summary>
		/// <value>The blending mode.</value>
		public Blending Blending {
			get { return _states.Blending; }
			set {
				if (_states.Blending != value) {
					_states.Blending = value;
					AddStateChanges(CanvasStateChanges.Blending);
				}
			}
		}

		/// <summary>
		/// Gets or sets the canvas pattern brush to use for primitive overlay drawing .
		/// </summary>
		/// <value>The pattern brush.</value>
		public CanvasBrush Brush {
			get { return _currentBrush; }
			set {
				_currentBrush = value;
				_states.BrushTexture = (null != _currentBrush ? _currentBrush.Pattern : null);
				AddStateChanges(CanvasStateChanges.BrushTexture);
			}
		}

		/// <summary>
		/// Gets or sets the alpha test state settings.
		/// </summary>
		/// <value>The alpha test.</value>
		public AlphaTest AlphaTest {
			get { return _states.AlphaTest; }
			set {
				if (value != _states.AlphaTest) {
					_states.AlphaTest = value;
					AddStateChanges(CanvasStateChanges.AlphaTest);
				}
			}
		}

		/// <summary>
		/// Gets or sets the Z effect state settings.
		/// </summary>
		/// <value>The Z effect.</value>
		public ZEffect ZEffect {
			get { return _states.ZEffect; }
			set {
				if (value != _states.ZEffect) {
					_states.ZEffect = value;
					AddStateChanges(CanvasStateChanges.ZEffect);
				}
			}
		}

		/// <summary>
		/// Gets or sets the rectangular canvas clipping.
		/// </summary>
		/// <value>The clipping.</value>
		public Clipping Clipping {
			get { return _states.Clipping; }
			set {
				if (value != _states.Clipping) {
					_states.Clipping = value;
					AddStateChanges(CanvasStateChanges.Clipping);
				}
			}
		}

		/// <summary>
		/// Gets or sets the <see cref="Vortex.Drawing.BaseTexture"/> for the specified sampler by index.
		/// </summary>
		/// <value><see cref="BaseTexture"/> which represents sampler state</value>
		public BaseTexture this[int samplerIndex] {
			get {
				CheckSamplerIndex(samplerIndex);
				return _states.Textures[samplerIndex];				
			}
			set {
				CheckSamplerIndex(samplerIndex);
				_states.Textures[samplerIndex] = value;
				AddStateChanges((CanvasStateChanges)((int)CanvasStateChanges.TextureBase << samplerIndex));
			}
		}

		/// <summary>
		/// Checks the sample index to be withing the defined range.
		/// </summary>
		/// <param name="samplerIndex">Index of the sampler.</param>
		private void CheckSamplerIndex(int samplerIndex) {
			if (samplerIndex < 0 || samplerIndex >= _states.Textures.Length) {
				throw new ArgumentException("Texture sampler index should be within range 0.." + _states.Textures.Length, "samplerIndex");
			}
		}

		#endregion

		/// <summary>
		/// For canvas : does nothing
		/// </summary>
		protected override void CloseScope() {
			;
		}

		/// <summary>
		/// Returns self, because it is root of scope chain.
		/// </summary>
		/// <value>The canvas instance.</value>
		protected override Canvas2D ReferredCanvas {
			get { return this; }
		}
	}
}
